#include "TestCaseManager.h"

#include "TestCase.h"

#include <iostream>

using namespace std;

const string NODE_NAME_CASES = "cases";
const string NODE_NAME_CASE = "case";
const string NODE_NAME_NAME = "name";
const string NODE_NAME_CONDITIONS = "conditions";
const string NODE_NAME_ARCH = "arch";
const string NODE_NAME_OS = "os";
const string NODE_NAME_VERSION = "version";
const string NODE_NAME_CHARSET = "charset";
const string NODE_NAME_PROGRAM = "program";
const string NODE_NAME_PCAP = "pcap";
const string NODE_NAME_RESULT = "result";
const string NODE_NAME_RESULTS = "results";
const string NODE_NAME_COMPARE_DATA = "compare_data";

/*******************************************************************************
*  FUNC     :   构造函数初始化一些成员变量
*  ARGS     :   xmlPath (case xml路径)
*  RTN      :
*  NOTE     :
*******************************************************************************/
TestCaseManager::TestCaseManager(const string &xmlPath)
    :xmlPath_(xmlPath), doc_(NULL)
{
}

/********************************************************************************
*  FUNC     :   析构函数释放libxml2 doc
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
TestCaseManager::~TestCaseManager()
{
    if (doc_)
        xmlFreeDoc(doc_);
}

/********************************************************************************
*  FUNC     :   解析case xml文件内容
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
bool TestCaseManager::Parse()
{
    if (doc_)
    {
        xmlFreeDoc(doc_);
        doc_ = NULL;
    }

    xmlKeepBlanksDefault(0);

    doc_ = xmlReadFile(xmlPath_.c_str(), NULL, 0);
    if (!doc_)
        return false;

    xmlNodePtr rootNode = xmlDocGetRootElement(doc_);
    if (!rootNode || string((const char*)rootNode->name) != NODE_NAME_CASES)
        return false;

    //解析每个case
    xmlNodePtr curNode = rootNode->children;
    for (; curNode != NULL; curNode = curNode->next)
    {
        if (curNode->type != XML_ELEMENT_NODE
                || string((const char*)curNode->name) != NODE_NAME_CASE)
            continue;

        if (!ParseOneCase(curNode))
            return false;
    }

    return true;
}

/********************************************************************************
*  FUNC     :   获取测试case数目
*  ARGS     :
*  RTN      :   case数目
*  NOTE     :
*******************************************************************************/
uint32_t TestCaseManager::GetTestCaseNum() const
{
    return cases_.size();
}

/********************************************************************************
*  FUNC     :   返回指定的测试case const引用
*  ARGS     :   idx (case的索引)
*  RTN      :   测试case const引用
*  NOTE     :
*******************************************************************************/
const TestCase &TestCaseManager::GetTestCase(uint32_t idx) const
{
    return cases_[idx];
}

/********************************************************************************
*  FUNC     :   返回指定的测试case引用
*  ARGS     :   idx (case的索引)
*  RTN      :   测试case引用
*  NOTE     :
*******************************************************************************/
TestCase &TestCaseManager::GetTestCase(uint32_t idx)
{
    return cases_[idx];
}

/********************************************************************************
*  FUNC     :   解析一个case内容
*  ARGS     :   node (xml节点，当前为case节点)
*  RTN      :   成功：true, 失败：false
*  NOTE     :
*******************************************************************************/
bool TestCaseManager::ParseOneCase(xmlNodePtr node)
{
    TestCase oneCase;

    xmlNodePtr curNode = node->children;
    for (; curNode != NULL; curNode = curNode->next)
    {
        if (curNode->type != XML_ELEMENT_NODE) continue;

        string nodeName((const char*)curNode->name);
        if (nodeName == NODE_NAME_NAME)
        {
            oneCase.SetName(XmlNodeGetContentEx(curNode));
        }
        else if (nodeName == NODE_NAME_CONDITIONS)
        {
            CaseConditions conditions;
            if (!ParseCaseConditions(curNode, conditions)) return false;

            oneCase.SetConditions(conditions);
        }
        else if (nodeName == NODE_NAME_PCAP)
        {
            oneCase.SetPcap(XmlNodeGetContentEx(curNode));
        }
        //else if (nodeName == NODE_NAME_RESULTS)
        //{
        //    if (!ParseResult(curNode, oneCase)) return false;
        //}
        else if (nodeName == NODE_NAME_RESULT)
        {
            string xmlPath = XmlNodeGetContentEx(curNode);
            if(xmlPath.length() == 0)
            {
                cout << "no result xml file" << endl;
                return false;
            }

            if(!ParseResult(xmlPath, oneCase)) return false;
        }
    }

    cases_.push_back(oneCase);

    return true;
}

/********************************************************************************
*  FUNC     :   解析case条件内容
*  ARGS     :   node (xml节点，当前为conditions节点)
*  RTN      :   成功：true, 失败：false
*  NOTE     :
*******************************************************************************/
bool TestCaseManager::ParseCaseConditions(xmlNodePtr node, CaseConditions &conditions)
{
    xmlNodePtr curNode = node->children;
    for (; curNode != NULL; curNode = curNode->next)
    {
        if (curNode->type != XML_ELEMENT_NODE) continue;

        string nodeName((const char*)curNode->name);
        if (nodeName == NODE_NAME_ARCH)
        {
            conditions.arch = XmlNodeGetContentEx(curNode);
        }
        else if (nodeName == NODE_NAME_OS)
        {
            conditions.os = XmlNodeGetContentEx(curNode);
        }
        else if (nodeName == NODE_NAME_VERSION)
        {
            conditions.version = XmlNodeGetContentEx(curNode);
        }
        else if (nodeName == NODE_NAME_CHARSET)
        {
            conditions.charset = XmlNodeGetContentEx(curNode);
        }
        else if (nodeName == NODE_NAME_PROGRAM)
        {
            conditions.program = XmlNodeGetContentEx(curNode);
        }
    }

    return true;
}

/********************************************************************************
*  FUNC     :   获取xml节点内容
*  ARGS     :   node (xml节点)
*  RTN      :   节点的内容
*  NOTE     :
*******************************************************************************/
string TestCaseManager::XmlNodeGetContentEx(xmlNodePtr node)
{
    xmlChar *value = xmlNodeGetContent(node);
    string nodeCont((const char*)value);
    xmlFree(value);

    return nodeCont;
}

/********************************************************************************
*  FUNC     :   解析case比对结果内容
*  ARGS     :   node (xml节点，当前为results节点)
*  RTN      :   成功：true, 失败：false
*  NOTE     :
*******************************************************************************/
bool TestCaseManager::ParseResult(const string &xmlPath, TestCase &oneCase)
{
    xmlDocPtr doc = xmlReadFile(xmlPath.c_str(), NULL, 0);
    if(!doc) return false;

    xmlNodePtr rootNode = xmlDocGetRootElement(doc);
    if(!rootNode || string((const char*)rootNode->name) != NODE_NAME_RESULTS)
    {
        xmlFreeDoc(doc);
        return false;
    }

    xmlNodePtr curNode = rootNode->children;
    for (; curNode != NULL; curNode = curNode->next)
    {
        if (curNode->type != XML_ELEMENT_NODE) continue;

        string nodeName((const char*)curNode->name);
        if (nodeName != NODE_NAME_COMPARE_DATA) continue;

        string cmpData = XmlNodeGetContentEx(curNode);
        oneCase.AddCompareData(cmpData);
    }

    xmlFreeDoc(doc);

    return true;
}

/********************************************************************************
*  FUNC     :   设置一个测试case
*  ARGS     :   oneCase (class TestCase)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void TestCaseManager::SetTestCase(const TestCase &oneCase)
{
    cases_.push_back(oneCase);
}

/********************************************************************************
*  FUNC     :   保存测试cases内容到xml文件中
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void TestCaseManager::Dump()
{
    doc_ = xmlNewDoc((const xmlChar*)"1.0");

    xmlNodePtr rootNode = xmlNewNode(NULL, (const xmlChar*)NODE_NAME_CASES.c_str());
    xmlDocSetRootElement(doc_, rootNode);

    uint32_t caseNum = cases_.size();
    for (uint32_t i = 0; i < caseNum; i++)
    {
        const TestCase &oneCase = GetTestCase(i);

        DumpOneCase(rootNode, oneCase);
    }

    xmlSaveFormatFileEnc(xmlPath_.c_str(), doc_, "UTF-8", 1);
}

/********************************************************************************
*  FUNC     :   保存case内容到xml节点
*  ARGS     :   node    （xml节点，当前为case节点)
                oneCase  (class TestCase)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void TestCaseManager::DumpOneCase(xmlNodePtr node, const TestCase &oneCase)
{
    xmlNodePtr caseNode = xmlNewChild(node, NULL, (const xmlChar*)NODE_NAME_CASE.c_str(),
            NULL);
    xmlNewChild(caseNode, NULL, (const xmlChar*)NODE_NAME_NAME.c_str(),
            (const xmlChar*)oneCase.GetName().c_str());

    DumpCaseConditions(caseNode, oneCase.GetConditions());

    xmlNewChild(caseNode, NULL, (const xmlChar*)NODE_NAME_PCAP.c_str(),
            (const xmlChar*)oneCase.GetPcap().c_str());

    string resultXml = oneCase.GetPcap() + ".res.xml";
    xmlNewChild(caseNode, NULL, (const xmlChar*)NODE_NAME_RESULT.c_str(),
            (const xmlChar*)resultXml.c_str());
    DumpCaseResult(resultXml, oneCase);
}

/********************************************************************************
*  FUNC     :   保存case条件内容到xml节点
*  ARGS     :   node    （xml节点，当前为case节点)
                conditions  (class CaseConditions)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void TestCaseManager::DumpCaseConditions(xmlNodePtr node, const CaseConditions &conditions)
{
    xmlNodePtr conditionsNode = xmlNewChild(node, NULL,
            (const xmlChar*)NODE_NAME_CONDITIONS.c_str(), NULL);

    if(conditions.arch.length() != 0)
        xmlNewChild(conditionsNode, NULL, (const xmlChar*)NODE_NAME_ARCH.c_str(),
                (const xmlChar*)conditions.arch.c_str());
    if(conditions.os.length() != 0)
        xmlNewChild(conditionsNode, NULL, (const xmlChar*)NODE_NAME_OS.c_str(),
                (const xmlChar*)conditions.os.c_str());
    if(conditions.version.length() != 0)
        xmlNewChild(conditionsNode, NULL, (const xmlChar*)NODE_NAME_VERSION.c_str(),
                (const xmlChar*)conditions.version.c_str());
    if(conditions.charset.length() != 0)
        xmlNewChild(conditionsNode, NULL, (const xmlChar*)NODE_NAME_CHARSET.c_str(),
                (const xmlChar*)conditions.charset.c_str());
    if(conditions.program.length() != 0)
        xmlNewChild(conditionsNode, NULL, (const xmlChar*)NODE_NAME_PROGRAM.c_str(),
                (const xmlChar*)conditions.program.c_str());
}

/********************************************************************************
*  FUNC     :   保存case比对内容到xml节点
*  ARGS     :   node    （xml节点，当前为case节点)
                oneCase  (class TestCase)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void TestCaseManager::DumpCaseResult(const string &xmlPath, const TestCase &oneCase)
{
    xmlDocPtr doc = xmlNewDoc((const xmlChar*)"1.0");
    xmlNodePtr rootNode = xmlNewNode(NULL, (const xmlChar*)NODE_NAME_RESULTS.c_str());
    xmlDocSetRootElement(doc, rootNode);

    uint32_t cmpDataNum = oneCase.GetCompareDatasNum();
    for(uint32_t i = 0; i < cmpDataNum; i++)
    {
        const string cmpData = oneCase.GetCompareData(i);
        xmlNodePtr cmpNode = xmlNewNode(NULL, (const xmlChar*)NODE_NAME_COMPARE_DATA.c_str());
        xmlNodePtr txtNode = xmlNewText((const xmlChar*)(const xmlChar*)cmpData.c_str());
        xmlAddChild(cmpNode, txtNode);
        xmlAddChild(rootNode, cmpNode);
    }

    xmlSaveFormatFileEnc(xmlPath.c_str(), doc, "UTF-8", 1);
    xmlFreeDoc(doc);
}
